package com.pig4cloud.pig.ads.service.media.bd;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baidu.dev2.api.sdk.invoke.ApiException;
import com.baidu.dev2.api.sdk.manual.oauth.api.OAuthService;
import com.baidu.dev2.api.sdk.manual.oauth.model.AccessTokenInfo;
import com.baidu.dev2.api.sdk.manual.oauth.model.GetAccessTokenRequest;
import com.baidu.dev2.api.sdk.manual.oauth.model.GetAccessTokenResponse;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.google.api.client.util.Lists;
import com.pig4cloud.pig.ads.service.media.AbstractMediaEngine;
import com.pig4cloud.pig.api.annotation.Media;
import com.pig4cloud.pig.api.dto.AdAccountResp;
import com.pig4cloud.pig.api.entity.AdAccesstoken;
import com.pig4cloud.pig.api.entity.AdOauthSetting;
import com.pig4cloud.pig.api.entity.ads.AdsCallBackResponse;
import com.pig4cloud.pig.api.entity.ads.MediaConstant;
import com.pig4cloud.pig.api.vo.AdAccountAgentVo;
import com.pig4cloud.pig.common.core.constant.enums.PlatformTypeEnum;
import com.pig4cloud.pig.common.core.util.AESUtils;
import com.pig4cloud.pig.common.core.util.R;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description
 * @Author chengang
 * @Date 2022/12/13
 */
@Slf4j
@Media(value = MediaConstant.BD)
public class BdMedia extends AbstractMediaEngine {

	private static final String AES_OFFSET = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";

	@SneakyThrows
	@Override
	public R callback(AdsCallBackResponse response, Map<String, Object> paramMap) {
		if(StringUtils.isAnyBlank(response.getState(), response.getAuthCode(),
				response.getAppId(),response.getSignature(),response.getUserId(),response.getTimestamp())) {
			return R.failed("百度授权回调返回结果不合法");
		}

		String stateStr = new String(Base64.getUrlDecoder().decode(response.getState()), StandardCharsets.UTF_8);
		Map<String,Object> stateMap = JSON.parseObject(stateStr).getInnerMap();
		Integer userId = (Integer) stateMap.get("userId");
		Integer agentId = (Integer) stateMap.get("agentId");
		log.info("回调state.userId:{}回调state.agentId{}",userId,agentId);

		//获取授权配置信息
		AdOauthSetting setting = super.getOauthSetting(PlatformTypeEnum.BD.getValue());
		if (setting == null) {
			return R.failed("开发者应用信息未配置，请联系技术人员。");
		}
		//验签
		if (!response.getSignature().equals(this.paramsSign(setting.getAppSecret(),response))) {
			return R.failed("签名错误，请联系技术人员。");
		}

		// 1.处理当前渠道AccessToken事宜
		// 获取AccessToken并入库 ad_accesstoken
		GetAccessTokenResponse accessTokenInfo = this.getAccessToken(setting, response);
		log.info("baidu AccessTokenInfo: {}",JSON.toJSONString(accessTokenInfo));
		if (null != accessTokenInfo && 0 == accessTokenInfo.getCode()){
			// 2.获取账户列表
			List<AdAccountAgentVo.Advertiser> advertisers = Lists.newArrayList();
			AdAccountResp housekeeperInfo = super.getAdvertiser(accessTokenInfo, advertisers);
			// 3.保存token
			AdAccesstoken adAccesstoken = this.dealBdAccessTokenInfo(accessTokenInfo, advertisers, setting, response.getAuthCode(), userId);
			super.saveAccessToken(adAccesstoken);
			if (CollectionUtils.isNotEmpty(advertisers)){
				// 4.处理账户表
				super.saveAdAccount(advertisers, housekeeperInfo, PlatformTypeEnum.BD.getValue());
				// 5.处理账户表
				super.saveAdvertiser(advertisers, housekeeperInfo, PlatformTypeEnum.BD.getValue());
				// 6.处理代理商
				super.saveAgent(advertisers, userId, agentId);
			}
		}else{
			log.info(">>>百度账户加挂失败：{}", accessTokenInfo.getMessage());
			return R.failed(accessTokenInfo.getMessage());
		}
		return R.ok();
	}

	/**
	 * 仅取当前渠道的参数作为签名因子
	 * @param response
	 * @return
	 */
	private TreeMap<String,Object> buildParams(AdsCallBackResponse response){
		TreeMap<String, Object> params = new TreeMap<>();
		params.put("appId",response.getAppId());
		params.put("authCode",response.getAuthCode());
		params.put("userId",response.getUserId());
		params.put("timestamp",response.getTimestamp());
		params.put("state",response.getState());
		TreeMap<String, Object> result = params.entrySet().stream()
				.sorted(Comparator.comparing(Map.Entry::getKey))
				.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (k1, k2) -> k1, TreeMap::new));
		return result;
	}

	@SneakyThrows
	private String paramsSign(String secretKey, AdsCallBackResponse response) {
		TreeMap<String,Object> map = buildParams(response);
		// 拼接成 json 字符串, 保证生成的json串字段顺序和Map中的顺序一致即可
		String json = JSONObject.toJSONString(map);
		// base64编码
		byte[] bytes = Base64.getEncoder()
				.encodeToString(json.getBytes(StandardCharsets.UTF_8))
				.getBytes(StandardCharsets.UTF_8);
		// AES加密 密钥为 应用sk 的前16位字符
		SecretKey keyAES = AESUtils.loadKeyAES(secretKey.substring(0, 16));
		return AESUtils.encryptAES(bytes, keyAES, AES_OFFSET);
	}

	private GetAccessTokenResponse getAccessToken(AdOauthSetting setting, AdsCallBackResponse callBackRes) throws ApiException {
		OAuthService oAuthService = new OAuthService();
		GetAccessTokenRequest request = new GetAccessTokenRequest();
		request.setAppId(setting.getAppId());
		request.setUserId(Long.valueOf(callBackRes.getUserId()));
		request.setAuthCode(callBackRes.getAuthCode());
		request.setSecretKey(setting.getAppSecret());
		GetAccessTokenResponse response = oAuthService.getAccessToken(request);
		return response;
	}
	 private AdAccesstoken dealBdAccessTokenInfo(GetAccessTokenResponse accessTokenResponse, List<AdAccountAgentVo.Advertiser> advertisers, AdOauthSetting setting, String authCode, Integer userId){
		Date date = new Date();
		 AccessTokenInfo accessTokenInfo = accessTokenResponse.getData();

		 AdAccesstoken adAccesstoken = new AdAccesstoken();
		 adAccesstoken.setMediaCode(PlatformTypeEnum.BD.getValue());
		 adAccesstoken.setAppId(setting.getAppId());
		 adAccesstoken.setAuthCode(authCode);
		 adAccesstoken.setAccessToken(accessTokenInfo.getAccessToken());
		 adAccesstoken.setRefreshToken(accessTokenInfo.getRefreshToken());
		 adAccesstoken.setExpiresIn(Long.valueOf(accessTokenInfo.getExpiresIn()));
		 adAccesstoken.setRefreshexpiresIn(Long.valueOf(accessTokenInfo.getRefreshExpiresIn()));
		 adAccesstoken.setExpiresTime(accessTokenInfo.getExpiresTime());
		 adAccesstoken.setRefreshexpiresTime(accessTokenInfo.getRefreshExpiresTime());
		 adAccesstoken.setAccountId(String.valueOf(accessTokenInfo.getUserId()));
		 adAccesstoken.setOpenId(accessTokenInfo.getOpenId());
		 adAccesstoken.setAdvertiserIds(JSON.toJSONString(advertisers));
		 adAccesstoken.setCreateId(Long.valueOf(userId));
		 adAccesstoken.setUpdateId(Long.valueOf(userId));
		 adAccesstoken.setTokenTime(date);
		 adAccesstoken.setCreateTime(date);
		 adAccesstoken.setUpdateTime(date);
		 return adAccesstoken;
	 }
}
